package com.mxw.gateway.config;

import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mxw.commons.errorcode.GlobalErrorCode;
import com.mxw.commons.exception.CustomException;
import com.mxw.commons.restful.Result;
import com.mxw.commons.utils.CustomExceptionUtil;
import com.mxw.gateway.errorcode.GatewayErrorCode;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/** @Description 网关全局异常处理 @Author RingKo @Date 2021/12/12 15:51 @Version 1.0 */
@Slf4j
@Order(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class GlobalErrorWebExceptionHandler implements ErrorWebExceptionHandler {

  private final ObjectMapper objectMapper;

  @Override
  public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
    ServerHttpResponse response = exchange.getResponse();
    if (response.isCommitted()) {
      return Mono.error(ex);
    }

    // 设置返回JSON
    response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
    if (ex instanceof ResponseStatusException) {
      response.setStatusCode(((ResponseStatusException) ex).getStatus());
    }

    return response.writeWith(
        Mono.fromSupplier(
            () -> {
              DataBufferFactory bufferFactory = response.bufferFactory();
              try {
                // 找不到服务
                if (ex instanceof NotFoundException) {
                  log.error(GatewayErrorCode.SERVICE_UNAVAILABLE.getLogString(null), ex);
                  return bufferFactory.wrap(
                      objectMapper.writeValueAsBytes(
                          Result.respondWithIErrorCode(GatewayErrorCode.SERVICE_UNAVAILABLE)));
                }
                // 自定义异常
                if (ex instanceof CustomException) {
                  CustomException customException = (CustomException) ex;
                  log.error(customException.getLogString(null), customException);
                  boolean whetherHideCustomErrorMessage =
                      CustomExceptionUtil.whetherHideCustomErrorMessage(customException);
                  if (whetherHideCustomErrorMessage) {
                    return bufferFactory.wrap(
                        objectMapper.writeValueAsBytes(
                            Result.respond(
                                customException.getCodeId(),
                                GlobalErrorCode.SYSTEM_ERROR.getMsg(),
                                null)));
                  }
                  return bufferFactory.wrap(
                      objectMapper.writeValueAsBytes(
                          Result.respondWithCustomException(customException)));
                }
                // 限流异常
                if (BlockException.isBlockException(ex)) {
                  log.error(GlobalErrorCode.SENTINEL_LIMIT.getLogString(null), ex);
                  return bufferFactory.wrap(
                      objectMapper.writeValueAsBytes(
                          Result.respondWithIErrorCode(GlobalErrorCode.SENTINEL_LIMIT)));
                }
                // 其他未知异常
                log.error(GlobalErrorCode.SYSTEM_ERROR.getLogString(null), ex);
                return bufferFactory.wrap(
                    objectMapper.writeValueAsBytes(
                        Result.respondWithIErrorCode(GlobalErrorCode.SYSTEM_ERROR)));
              } catch (JsonProcessingException e) {
                log.error("Error writing response", ex);
                return bufferFactory.wrap(new byte[0]);
              }
            }));
  }
}
